﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

using Emgu.CV;
using Emgu.Util;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using System.Runtime.InteropServices;
using Emgu.CV.Features2D;
using Emgu.CV.Util;


namespace CVsift
{
    public partial class RealizeSIFT : Form
    {
        public RealizeSIFT()
        {
            InitializeComponent();
        }

        public static Bitmap ConvertIntPrToBitmap(IntPtr ptrImage)
        {
            //将IplImage指针转换成MIplImage结构
            MIplImage mi = (MIplImage)Marshal.PtrToStructure(ptrImage, typeof(MIplImage));
            Image<Bgr, Byte> image = new Image<Bgr, Byte>(mi.width, mi.height, mi.widthStep, mi.imageData);
            return image.ToBitmap();
        }

        private void OnepicOpen_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                IntPtr ptrImage = CvInvoke.cvLoadImage(openFileDialog1.FileName, LOAD_IMAGE_TYPE.CV_LOAD_IMAGE_UNCHANGED);
                /* IplImage* 转换为Bitmap */
                OnepicBox.Image = ConvertIntPrToBitmap(ptrImage);
            }
            if (TwopicBox.Image != null)
            {
                Search.Enabled = true;
            }
        }

        private void TwopicOpen_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                IntPtr ptrImage = CvInvoke.cvLoadImage(openFileDialog1.FileName, LOAD_IMAGE_TYPE.CV_LOAD_IMAGE_UNCHANGED);
                /* IplImage* 转换为Bitmap */
                TwopicBox.Image = ConvertIntPrToBitmap(ptrImage);
            }
            if (OnepicBox.Image != null)
            {
                Search.Enabled = true;
            }
        }

        //EmguCV里用surf算法的示例
        //public static Image<Bgr, Byte> Draw(string modelImageFileName, string observedImageFileName, out long matchTime)
        //{
        //    Image<Gray, Byte> modelImage = new Image<Gray, byte>(modelImageFileName);
        //    Image<Gray, Byte> observedImage = new Image<Gray, byte>(observedImageFileName);
        //    Stopwatch watch;
        //    HomographyMatrix homography = null;

        //    SURFDetector surfCPU = new SURFDetector(500, false);
        //    VectorOfKeyPoint modelKeyPoints;
        //    VectorOfKeyPoint observedKeyPoints;
        //    Matrix<int> indices;

        //    Matrix<byte> mask;
        //    int k = 2;
        //    double uniquenessThreshold = 0.8;

        //    //extract features from the object image
        //    modelKeyPoints = surfCPU.DetectKeyPointsRaw(modelImage, null);
        //    Matrix<float> modelDescriptors = surfCPU.ComputeDescriptorsRaw(modelImage, null, modelKeyPoints);

        //    watch = Stopwatch.StartNew();

        //    // extract features from the observed image
        //    observedKeyPoints = surfCPU.DetectKeyPointsRaw(observedImage, null);
        //    if (observedKeyPoints.Size == 0)
        //        throw new Exception();

        //    Matrix<float> observedDescriptors = surfCPU.ComputeDescriptorsRaw(observedImage, null, observedKeyPoints);
        //    BruteForceMatcher<float> matcher = new BruteForceMatcher<float>(DistanceType.L2);
        //    matcher.Add(modelDescriptors);

        //    indices = new Matrix<int>(observedDescriptors.Rows, k);
        //    using (Matrix<float> dist = new Matrix<float>(observedDescriptors.Rows, k))
        //    {
        //        matcher.KnnMatch(observedDescriptors, indices, dist, k, null);
        //        mask = new Matrix<byte>(dist.Rows, 1);
        //        mask.SetValue(255);
        //        Features2DToolbox.VoteForUniqueness(dist, uniquenessThreshold, mask);
        //    }

        //    int nonZeroCount = CvInvoke.cvCountNonZero(mask);
        //    if (nonZeroCount >= 4)
        //    {
        //        nonZeroCount = Features2DToolbox.VoteForSizeAndOrientation(modelKeyPoints, observedKeyPoints, indices, mask, 1.5, 20);
        //        if (nonZeroCount >= 4)
        //            homography = Features2DToolbox.GetHomographyMatrixFromMatchedFeatures(modelKeyPoints, observedKeyPoints, indices, mask, 2);
        //    }

        //    watch.Stop();

        //    //Draw the matched keypoints
        //    Image<Bgr, Byte> result = Features2DToolbox.DrawMatches(modelImage, modelKeyPoints, observedImage, observedKeyPoints,
        //       indices, new Bgr(255, 255, 255), new Bgr(255, 255, 255), mask, Features2DToolbox.KeypointDrawType.DEFAULT);

        //    #region draw the projected region on the image
        //    if (homography != null)
        //    {  //draw a rectangle along the projected model
        //        Rectangle rect = modelImage.ROI;
        //        PointF[] pts = new PointF[] { 
        //       new PointF(rect.Left, rect.Bottom),
        //       new PointF(rect.Right, rect.Bottom),
        //       new PointF(rect.Right, rect.Top),
        //       new PointF(rect.Left, rect.Top)};
        //        homography.ProjectPoints(pts);

        //        result.DrawPolyline(Array.ConvertAll<PointF, Point>(pts, Point.Round), true, new Bgr(Color.Red), 5);
        //    }
        //    #endregion

        //    matchTime = watch.ElapsedMilliseconds;

        //    return result;
        //}  

        public int s(int i)
        {
            if (i == 0)
            {
                textBox.Text = "没有相似处";
            }
            if (i == 1)
            {
                textBox.Text = "有相似处";
            }
            return i;
        }

        private void Search_Click(object sender, EventArgs e)
        {
            int p = 0;
            //确定匹配图像
            Bitmap bt1 = new Bitmap(OnepicBox.Image);
            Bitmap bt2 = new Bitmap(TwopicBox.Image);
            //将图像转为Emgu cv的处理格式
            Image<Gray, byte> img1 = new Image<Gray, byte>(bt1);
            Image<Gray, byte> img2 = new Image<Gray, byte>(bt2);
            //使用Emgu cv探测图片特征点
            SIFTDetector sift = new SIFTDetector();
            var f1 = sift.DetectFeatures(img1, null);
            var f2 = sift.DetectFeatures(img2, null);
            //到此已经获得了两张相片的特征点f1,f2,接下来就是将相互匹配的特征点相连:
            for (int i = 0; i < f1.Length; i++)
            {
                double[] dist = new double[f2.Length];
                int n = 0;
                int index = 0;
                for (int j = 0; j < f2.Length; j++)
                {
                    //计算待比较特征点描述子的欧式距离                    
                    dist[n++] = diedai(f1[i].Descriptor, f2[j].Descriptor);
                }
                //排序,获得欧式距离最近点以及次近点                
                for (int k = 0; k < 2; k++)
                {
                    for (int k1 = k + 1; k1 < dist.Length; k1++)
                    {
                        if (dist[k] > dist[k1])
                        {
                            double temp = dist[k];
                            dist[k] = dist[k1];
                            dist[k1] = temp;
                            //记录最近点在相片2中的序列                           
                            if (k == 0)
                            {
                                index = k1;
                            }
                        }
                    }
                }
                //若最近点与次近点比值小于阈值0.49,绘制特征点连线               
                if (dist[0] / dist[1] < 0.49)
                {
                    PointF point1 = f1[i].KeyPoint.Point;
                    PointF point2 = f2[index].KeyPoint.Point;
                    Graphics g = this.CreateGraphics();
                    Pen p1 = new Pen(Color.Red, 3);
                    g.DrawLine(p1, point1, point2);
                    p++;
                }
            }
            if (p > 10)
            {
                s(1);
            }
            else
            {
                s(0);
            }
        }
        //注意:point1,point2位置与具体控件,图像布局有关,请自行调整 
        //描述子距离计算函数
        private double diedai(float[] p1, float[] p2)
        {
            double sub = 0;
            double sqrt = 0;
            for (int i = 0; i < p1.Length; i++)
            {
                sub += Math.Pow(p1[i] - p2[i], 2);
            }
            sqrt = Math.Sqrt(sub);
            return sqrt;
        }
    }
}             
            
